/*
 * Copyright (c) 2022, IMMORTA Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 * - Redistributions of source code must retain the above copyright notice, this list
 *   of conditions and the following disclaimer.
 *
 * - Redistributions in binary form must reproduce the above copyright notice, this
 *   list of conditions and the following disclaimer in the documentation and/or
 *   other materials provided with the distribution.
 *
 * - Neither the name of IMMORTA Inc. nor the names of its
 *   contributors may be used to endorse or promote products derived from this
 *   software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/*!
 * @file rtc_drv.c
 * @brief This file defines rtc driver functions
 */

/*******Includes***************************************************************/
#include <stdint.h>
#include <stddef.h>
#include "rtc_reg_access.h"

/*******Definitions************************************************************/
#define SECONDS_OF_A_MIN       (60U)
#define MINS_OF_AN_HOUR        (60U)
#define HOURS_OF_A_DAY         (24U)
#define SECONDS_OF_AN_HOUR     (MINS_OF_AN_HOUR * SECONDS_OF_A_MIN)
#define SECONDS_OF_A_DAY       (HOURS_OF_A_DAY * SECONDS_OF_AN_HOUR)
#define DAYS_OF_A_COMMON_YEAR  (365U)
#define DAYS_OF_A_LEAP_YEAR    (366U)
#define UNIX_TIMESTAMP_START   (1970U)
#define UNIX_TIMESTAMP_END     (2106U)

/* Default clock presacle value of RTC, default clock source is 32000 internal */
#define DEFAULT_PRESCALE_VALUE (31999U)

/* Days table for non-leap year, days of January is at index 1, and so on*/
static const uint8_t COMMON_YEAR_MONTHS[] = {0U, 31U, 28U, 31U, 30U, 31U, 30U,
                                             31U, 31U, 30U, 31U, 30U, 31U
                                            };

/* Days table for leap year, days of January is at index 1, and so on*/
static const uint8_t LEAP_YEAR_MONTHS[] = {0U, 31U, 29U, 31U, 30U, 31U, 30U,
                                           31U, 31U, 30U, 31U, 30U, 31U
                                          };

/* Days elapsed from the begin of a non-leap year*/
static const uint16_t COMMON_YEAR_MONTH_DAYS[] = {0U, 0U, 31U, 59U, 90U, 120U, 151U,
                                                  181U, 212U, 243U, 273U, 304U, 334U
                                                 };

/*******Variables**************************************************************/
/*! @brief Table to save register base addresses for RTC instances. */
static RTC_Type* const s_rtcRegBase[CONFIG_RTC_INSTANCE_COUNT] = CONFIG_RTC_REG_BASE_PTRS;
/*! @brief Table to save interrupt ids for RTC instances */
static const IRQn_Type s_rtcIrqId[CONFIG_RTC_INSTANCE_COUNT] = CONFIG_RTC_IRQS;

static struct {
    bool alarmRepeat;
    rtc_alarm_config_t* alarmConfig;
    rtc_periodic_alarm_config_t* periodicAlarmConfig;
    rtc_overflow_config_t* overflowConfig;
} s_rtcRunTimeConfig[CONFIG_RTC_INSTANCE_COUNT];

/*******Prototypes*************************************************************/
static void RTC_AlarmIRQHandlerProcess(uint32_t instance);
static void RTC_PeriodicAlarmIRQHandlerProcess(uint32_t instance);
static void RTC_OverflowIRQHandlerProcess(uint32_t instance);

/*******Private Functions******************************************************/

/*******Code*******************************************************************/

/*******************************************************************************
 *** Function Name : RTC_Init
 *** Description   : Initialize RTC instance via the given configuration
 ******************************************************************************/
errcode_t RTC_Init(uint32_t instance, const rtc_config_t* config)
{
    ASSERT_PARAM(instance < CONFIG_RTC_INSTANCE_COUNT);
    ASSERT_PARAM(config != NULL);

    errcode_t errCode = ERR_SUCCESS;
    RTC_Type* rtcBase = s_rtcRegBase[instance];

    if (RTC_REG_IsRTCEnabled(s_rtcRegBase[instance])) {
        errCode = ERR_RTC_ALREADY_RUNNING;
    } else {
        NVIC_DisableIRQ(s_rtcIrqId[instance]);
        NVIC_ClearPendingIRQ(s_rtcIrqId[instance]);

        s_rtcRunTimeConfig[instance].alarmRepeat = false;
        s_rtcRunTimeConfig[instance].alarmConfig = NULL;
        s_rtcRunTimeConfig[instance].periodicAlarmConfig = NULL;
        s_rtcRunTimeConfig[instance].overflowConfig = NULL;

        /* Select RTC clock source */
        RTC_REG_SelectClockSource(rtcBase, config->clockSoruce);

        /* Configure RTC  */
        if ((ERR_SUCCESS == (errCode = RTC_REG_SetCounterValue(rtcBase, 0U))) &&
                (ERR_SUCCESS == (errCode = RTC_REG_SetPrescalerValue(rtcBase, config->prescaler)))) {
            /* Configure clock out*/
            RTC_REG_SelectClockOutSource(rtcBase, config->clockOutSelect);
            RTC_REG_SetClockOutEnable(rtcBase, config->clockOutEnable);

            /* Configure trigger*/
            RTC_REG_SetTriggerEnable(rtcBase, config->ICMTriggerEnable);

            /* Configure compensation*/
            if (config->compensation != 0) {
                if (((int16_t)config->prescaler + config->compensation) <= 0) {
                    errCode = ERR_RTC_INVALID_COMP_RANGE;
                } else {
                    RTC_REG_SetCompensation(rtcBase, config->compensation,
                                            config->compensationInterval);
                }
            }
        }
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : RTC_Deinit
 *** Description   : Deinitialize RTC instance
 ******************************************************************************/
void RTC_Deinit(uint32_t instance)
{
    ASSERT_PARAM(instance < CONFIG_RTC_INSTANCE_COUNT);

    RTC_Type* rtcBase = s_rtcRegBase[instance];

    NVIC_DisableIRQ(s_rtcIrqId[instance]);
    NVIC_ClearPendingIRQ(s_rtcIrqId[instance]);

    /* Reset CFG, ALM, PALM, PSCAL, ICOMP, INT, SCNT register */
    /* Reset CFG register */
    RTC_REG_SetRTCEnable(rtcBase, false);
    RTC_REG_SelectClockOutSource(rtcBase, RTC_CLK_OUT_PASS_THROUGH_CLK_IN);
    RTC_REG_SetClockOutEnable(rtcBase, false);
    RTC_REG_SetAlarmEnable(rtcBase, false);
    RTC_REG_SetPeriodicAlarmEnable(rtcBase, false);
    RTC_REG_SetTriggerEnable(rtcBase, false);
    RTC_REG_SelectClockSource(rtcBase, RTC_CLK_SRC_LSI);

    /* Reset ALM register */
    RTC_REG_SetAlarmTarget(rtcBase, 0U);

    /* Reset PALM register */
    RTC_REG_SetPeriodicAlarmInterval(rtcBase, 0U);

    /* Reset PSCAL register */
    (void)RTC_REG_SetPrescalerValue(rtcBase, DEFAULT_PRESCALE_VALUE);

    /* Reset ICOMP register */
    RTC_REG_SetCompensation(rtcBase, 0, 0U);

    /* Reset INT register */
    RTC_REG_SetAlarmIntEnable(rtcBase, false);
    RTC_REG_SetPeriodicAlarmIntEnable(rtcBase, false);
    RTC_REG_SetOverflowIntEnable(rtcBase, false);

    /* Reset SCNT register */
    (void)RTC_REG_SetCounterValue(rtcBase, 0U);

    /* Reset software status */
    s_rtcRunTimeConfig[instance].alarmRepeat = false;
    s_rtcRunTimeConfig[instance].alarmConfig = NULL;
    s_rtcRunTimeConfig[instance].periodicAlarmConfig = NULL;
    s_rtcRunTimeConfig[instance].overflowConfig = NULL;
}

/*******************************************************************************
 *** Function Name : RTC_GetDefaultConfig
 *** Description   : Get default RTC configuration via the configuration
 ***                 value passed as parameter
 ******************************************************************************/
void RTC_GetDefaultConfig(rtc_config_t* config)
{
    ASSERT_PARAM(config != NULL);

    config->clockSoruce = RTC_CLK_SRC_LSI;
    config->prescaler = DEFAULT_PRESCALE_VALUE;
    config->compensation = 0;
    config->compensationInterval = 0U;
    config->clockOutSelect = RTC_CLK_OUT_PRESCALED_CLK;
    config->clockOutEnable = false;
    config->ICMTriggerEnable = false;
}

/*******************************************************************************
 *** Function Name : RTC_SetTimeDate
 *** Description   : Set counter of RTC according to the given time
 ******************************************************************************/
errcode_t RTC_SetTimeDate(uint32_t instance, const rtc_timedate_t* timeDate)
{
    ASSERT_PARAM(instance < CONFIG_RTC_INSTANCE_COUNT);
    ASSERT_PARAM(timeDate != NULL);

    errcode_t errCode = ERR_SUCCESS;
    uint32_t seconds = 0U;

    if (RTC_REG_IsRTCEnabled(s_rtcRegBase[instance])) {
        errCode = ERR_RTC_INVALID_STATE;
    } else if (RTC_IsTimeDateCorrectFormat(timeDate) == false) {
        errCode = ERR_RTC_WRONG_TIMEDATE;
    } else {
        RTC_ConvertTimeDateToSeconds(timeDate, &seconds);
        errCode = RTC_REG_SetCounterValue(s_rtcRegBase[instance], seconds);
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : RTC_GetCurrentTimeDate
 *** Description   : Get current time according to counter of RTC
 ******************************************************************************/
errcode_t RTC_GetCurrentTimeDate(uint32_t instance, rtc_timedate_t* timeDate)
{
    ASSERT_PARAM(instance < CONFIG_RTC_INSTANCE_COUNT);
    ASSERT_PARAM(timeDate != NULL);

    uint32_t seconds;
    uint32_t secondsCheck;
    errcode_t errCode = ERR_SUCCESS;

    secondsCheck = RTC_REG_GetCounterValue(s_rtcRegBase[instance]);
    seconds = RTC_REG_GetCounterValue(s_rtcRegBase[instance]);

    if (seconds != secondsCheck) {
        secondsCheck = RTC_REG_GetCounterValue(s_rtcRegBase[instance]);

        if (seconds != secondsCheck) {
            errCode = ERR_RTC_CLOCK_DOMAIN_SYNC_ERROR;
        }
    }

    if (errCode == ERR_SUCCESS) {
        RTC_ConvertSecondsToTimeDate(&seconds, timeDate);
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : RTC_StartCounter
 *** Description   : Start counter of RTC
 ******************************************************************************/
errcode_t RTC_StartCounter(uint32_t instance)
{
    ASSERT_PARAM(instance < CONFIG_RTC_INSTANCE_COUNT);

    errcode_t errCode = ERR_SUCCESS;

    if (RTC_REG_IsRTCEnabled(s_rtcRegBase[instance]) == false) {
        RTC_REG_SetRTCEnable(s_rtcRegBase[instance], true);
    } else {
        errCode = ERR_RTC_INVALID_STATE;
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : RTC_StopCounter
 *** Description   : Stop counter of RTC
 ******************************************************************************/
errcode_t RTC_StopCounter(uint32_t instance)
{
    ASSERT_PARAM(instance < CONFIG_RTC_INSTANCE_COUNT);

    errcode_t errCode = ERR_SUCCESS;

    if (RTC_REG_IsRTCEnabled(s_rtcRegBase[instance]) == true) {
        RTC_REG_SetRTCEnable(s_rtcRegBase[instance], false);
    }

    errCode = RTC_REG_IsRTCEnabled(s_rtcRegBase[instance]) ? ERR_ERROR : ERR_SUCCESS;

    return errCode;
}

/*******************************************************************************
 *** Function Name : RTC_IsEnabled
 *** Description   : Check whether RTC is enabled or not
 ******************************************************************************/
bool RTC_IsEnabled(uint32_t instance)
{
    return RTC_REG_IsRTCEnabled(s_rtcRegBase[instance]);
}

/*******************************************************************************
 *** Function Name : RTC_ConfigTimeCompensation
 *** Description   : Set compensation value of RTC
 ******************************************************************************/
errcode_t RTC_ConfigTimeCompensation(uint32_t instance, int8_t compensation, uint8_t interval)
{
    ASSERT_PARAM(instance < CONFIG_RTC_INSTANCE_COUNT);

    errcode_t errCode = ERR_SUCCESS;
    uint16_t prescalerValue = RTC_REG_GetPrescalerValue(s_rtcRegBase[instance]);

    /* If minus compensation cause precaler overflow, prescaler will be 0x3FFFF */
    if (((int16_t)prescalerValue + compensation) <= 0) {
        errCode = ERR_RTC_INVALID_COMP_RANGE;
    } else {
        RTC_REG_SetCompensation(s_rtcRegBase[instance], compensation, interval);
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : RTC_GetTimeCompensation
 *** Description   : Get compensation value of RTC
 ******************************************************************************/
void RTC_GetTimeCompensation(uint32_t instance, int8_t* compensation,  uint8_t* interval)
{
    ASSERT_PARAM(instance < CONFIG_RTC_INSTANCE_COUNT);
    ASSERT_PARAM(compensation != NULL);
    ASSERT_PARAM(interval != NULL);

    RTC_REG_GetCompensation(s_rtcRegBase[instance], compensation, interval);
}

/*******************************************************************************
 *** Function Name : RTC_ConfigAlarm
 *** Description   : Set alarm of RTC via configuration passed as parameter
 ******************************************************************************/
errcode_t RTC_ConfigAlarm(uint32_t instance, rtc_alarm_config_t* alarmConfig)
{
    ASSERT_PARAM(instance < CONFIG_RTC_INSTANCE_COUNT);
    ASSERT_PARAM(alarmConfig != NULL);

    errcode_t errCode = ERR_SUCCESS;
    uint32_t targetSeconds = 0U;

    if (RTC_IsTimeDateCorrectFormat(&alarmConfig->alarmTime)) {
        RTC_ConvertTimeDateToSeconds(&alarmConfig->alarmTime, &targetSeconds);

        NVIC_DisableIRQ(s_rtcIrqId[instance]);

        s_rtcRunTimeConfig[instance].alarmConfig = alarmConfig;
        s_rtcRunTimeConfig[instance].alarmRepeat = false;

        RTC_REG_SetAlarmTarget(s_rtcRegBase[instance], targetSeconds);
        RTC_REG_SetAlarmIntEnable(s_rtcRegBase[instance], alarmConfig->alarmIntEnable);
        RTC_REG_SetAlarmEnable(s_rtcRegBase[instance], true);

        NVIC_EnableIRQ(s_rtcIrqId[instance]);
    } else {
        errCode = ERR_RTC_WRONG_TIMEDATE;
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : RTC_StopAlarm
 *** Description   : Stop alarm of RTC
 ******************************************************************************/
void RTC_StopAlarm(uint32_t instance)
{
    ASSERT_PARAM(instance < CONFIG_RTC_INSTANCE_COUNT);

    RTC_REG_SetAlarmTarget(s_rtcRegBase[instance], 0U);
    RTC_REG_SetAlarmIntEnable(s_rtcRegBase[instance], false);
    RTC_REG_SetAlarmEnable(s_rtcRegBase[instance], false);
}

/*******************************************************************************
 *** Function Name : RTC_GetAlarmConfig
 *** Description   : Get alarm configuration of RTC
 ******************************************************************************/
errcode_t RTC_GetAlarmConfig(uint32_t instance, rtc_alarm_config_t* alarmConfig)
{
    ASSERT_PARAM(instance < CONFIG_RTC_INSTANCE_COUNT);
    ASSERT_PARAM(alarmConfig != NULL);

    *alarmConfig = *s_rtcRunTimeConfig[instance].alarmConfig;

    return ERR_SUCCESS;
}

/*******************************************************************************
 *** Function Name : RTC_IsAlarmPending
 *** Description   : Check if a alarm event is pending
 ******************************************************************************/
bool RTC_IsAlarmPending(uint32_t instance)
{
    ASSERT_PARAM(instance < CONFIG_RTC_INSTANCE_COUNT);

    return RTC_REG_GetAlarmIntFlag(s_rtcRegBase[instance]);
}

/*******************************************************************************
 *** Function Name : RTC_GetNextAlarmTime
 *** Description   : Get the time of next on going alarm
 ******************************************************************************/
errcode_t RTC_GetNextAlarmTime(uint32_t instance, rtc_timedate_t* alarmTime)
{
    ASSERT_PARAM(instance < CONFIG_RTC_INSTANCE_COUNT);
    ASSERT_PARAM(alarmTime != NULL);

    uint32_t seconds;
    errcode_t errCode = ERR_SUCCESS;

    if (s_rtcRunTimeConfig[instance].alarmRepeat == true) {
        seconds = RTC_REG_GetAlarmTarget(s_rtcRegBase[instance]);
        RTC_ConvertSecondsToTimeDate(&seconds, alarmTime);
    } else {
        errCode = ERR_ERROR;
    }

    return errCode;
}

/*******************************************************************************
 *** Function Name : RTC_ConfigPeriodicAlarm
 *** Description   : Set periodic alarm of RTC via configuration passed as parameter
 ******************************************************************************/
errcode_t RTC_ConfigPeriodicAlarm(uint32_t instance, rtc_periodic_alarm_config_t* alarmConfig)
{
    ASSERT_PARAM(instance < CONFIG_RTC_INSTANCE_COUNT);
    ASSERT_PARAM(alarmConfig != NULL);

    NVIC_DisableIRQ(s_rtcIrqId[instance]);

    s_rtcRunTimeConfig[instance].periodicAlarmConfig = alarmConfig;

    RTC_REG_SetPeriodicAlarmInterval(s_rtcRegBase[instance], alarmConfig->interval);
    RTC_REG_SetPeriodicAlarmIntEnable(s_rtcRegBase[instance], alarmConfig->periodicAlarmIntEnable);
    RTC_REG_SetPeriodicAlarmEnable(s_rtcRegBase[instance], true);

    NVIC_EnableIRQ(s_rtcIrqId[instance]);

    return ERR_SUCCESS;
}

/*******************************************************************************
 *** Function Name : RTC_StopPeriodicAlarm
 *** Description   : Stop periodic alarm of RTC
 ******************************************************************************/
void RTC_StopPeriodicAlarm(uint32_t instance)
{
    ASSERT_PARAM(instance < CONFIG_RTC_INSTANCE_COUNT);

    RTC_REG_SetPeriodicAlarmInterval(s_rtcRegBase[instance], 0U);
    RTC_REG_SetPeriodicAlarmIntEnable(s_rtcRegBase[instance], false);
    RTC_REG_SetPeriodicAlarmEnable(s_rtcRegBase[instance], false);
}

/*******************************************************************************
 *** Function Name : RTC_GetPeriodicAlarmConfig
 *** Description   : Get periodic alarm configuration of RTC
 ******************************************************************************/
errcode_t RTC_GetPeriodicAlarmConfig(uint32_t instance, rtc_periodic_alarm_config_t* alarmConfig)
{
    ASSERT_PARAM(instance < CONFIG_RTC_INSTANCE_COUNT);
    ASSERT_PARAM(alarmConfig != NULL);

    *alarmConfig = *s_rtcRunTimeConfig[instance].periodicAlarmConfig;

    return ERR_SUCCESS;
}

/*******************************************************************************
 *** Function Name : RTC_ConfigOverflow
 *** Description   : Set overflow of RTC via configuration passed as parameter
 ******************************************************************************/
void RTC_ConfigOverflow(uint32_t instance, rtc_overflow_config_t* overflowConfig)
{
    ASSERT_PARAM(instance < CONFIG_RTC_INSTANCE_COUNT);
    ASSERT_PARAM(overflowConfig != NULL);

    NVIC_DisableIRQ(s_rtcIrqId[instance]);

    s_rtcRunTimeConfig[instance].overflowConfig = overflowConfig;

    RTC_REG_SetOverflowIntEnable(s_rtcRegBase[instance], overflowConfig->overflowIntEnable);

    NVIC_EnableIRQ(s_rtcIrqId[instance]);
}

/*******************************************************************************
 *** Function Name : RTC_ConvertSecondsToTimeDate
 *** Description   : Convert seconds to Time/Date
 ******************************************************************************/
void RTC_ConvertSecondsToTimeDate(const uint32_t* seconds, rtc_timedate_t* timeDate)
{
    ASSERT_PARAM(seconds != NULL);
    ASSERT_PARAM(timeDate != NULL);

    uint32_t dayCounts;
    uint32_t tmpSeconds;
    uint32_t daysOfAYear;
    const uint8_t* daysOfEachMonth;
    uint16_t monthIdx;
    uint16_t daysInCurrentMonth;

    dayCounts = *seconds / SECONDS_OF_A_DAY;
    tmpSeconds = *seconds % SECONDS_OF_A_DAY;

    /* Get time: hour, minutes, seconds */
    timeDate->hour = (uint16_t)(tmpSeconds / SECONDS_OF_AN_HOUR);
    tmpSeconds = tmpSeconds % SECONDS_OF_AN_HOUR;

    timeDate->minutes = (uint16_t)(tmpSeconds / SECONDS_OF_A_MIN);
    timeDate->seconds = (uint8_t)(tmpSeconds % SECONDS_OF_A_MIN);

    /* Get date: year, month, day */
    timeDate->year = UNIX_TIMESTAMP_START;
    daysOfAYear = DAYS_OF_A_COMMON_YEAR;

    /* Because 1970 is a non-leap year, so the loop start with a common year */
    while (dayCounts >= daysOfAYear) {
        timeDate->year += 1U;
        dayCounts -= daysOfAYear;

        daysOfAYear = RTC_IsLeapYear(timeDate->year) ? DAYS_OF_A_LEAP_YEAR : DAYS_OF_A_COMMON_YEAR;
    }

    dayCounts += 1U;

    daysOfEachMonth = RTC_IsLeapYear(timeDate->year) ? LEAP_YEAR_MONTHS : COMMON_YEAR_MONTHS;

    for (monthIdx = 1U; monthIdx <= 12U; monthIdx++) {
        daysInCurrentMonth = daysOfEachMonth[monthIdx];

        if (dayCounts <= daysInCurrentMonth) {
            timeDate->month = monthIdx;
            break;
        } else {
            dayCounts -= daysInCurrentMonth;
        }
    }

    timeDate->day = (uint16_t)dayCounts;
}

/*******************************************************************************
 *** Function Name : RTC_ConvertTimeDateToSeconds
 *** Description   : Convert Time/Date to seconds
 ******************************************************************************/
void RTC_ConvertTimeDateToSeconds(const rtc_timedate_t* timeDate, uint32_t* seconds)
{
    ASSERT_PARAM(timeDate != NULL);
    ASSERT_PARAM(seconds != NULL);

    uint32_t totalSeconds;
    uint16_t yearIdx;

    /* Non-leap year seconds */
    totalSeconds = (timeDate->year - UNIX_TIMESTAMP_START) * DAYS_OF_A_COMMON_YEAR * SECONDS_OF_A_DAY;
    totalSeconds += COMMON_YEAR_MONTH_DAYS[timeDate->month] * SECONDS_OF_A_DAY;
    totalSeconds += (timeDate->day - 1U) * SECONDS_OF_A_DAY;
    totalSeconds += timeDate->hour * SECONDS_OF_AN_HOUR;
    totalSeconds += timeDate->minutes * SECONDS_OF_A_MIN;
    totalSeconds += timeDate->seconds;

    /* Add leap year seconds */
    for (yearIdx = UNIX_TIMESTAMP_START; yearIdx < timeDate->year; yearIdx++) {
        if (RTC_IsLeapYear(yearIdx)) {
            totalSeconds += SECONDS_OF_A_DAY;
        }
    }

    if (RTC_IsLeapYear(timeDate->year) && (timeDate->month > 2U)) {
        totalSeconds += SECONDS_OF_A_DAY;
    }

    *seconds = totalSeconds;
}

/*******************************************************************************
 *** Function Name : RTC_IsTimeDateCorrectFormat
 *** Description   : Check if the Time/Date is in configured correctly
 ******************************************************************************/
bool RTC_IsTimeDateCorrectFormat(const rtc_timedate_t* timeDate)
{
    ASSERT_PARAM(timeDate != NULL);

    bool correct = false;
    const uint8_t* days = RTC_IsLeapYear(timeDate->year) ? LEAP_YEAR_MONTHS : COMMON_YEAR_MONTHS;

    if ((timeDate->year < UNIX_TIMESTAMP_START) || (timeDate->year > UNIX_TIMESTAMP_END)
            || (timeDate->month < 1U) || (timeDate->month > 12U)
            || (timeDate->day < 1U) || (timeDate->day > days[timeDate->month])
            || (timeDate->hour >= HOURS_OF_A_DAY)
            || (timeDate->minutes >= MINS_OF_AN_HOUR)
            || (timeDate->seconds > SECONDS_OF_A_MIN)) {
        correct = false;
    } else {
        correct = true;
    }

    return correct;
}

/*******************************************************************************
 *** Function Name : RTC_IsLeapYear
 *** Description   : Check if the given year is leap
 ******************************************************************************/
bool RTC_IsLeapYear(uint32_t year)
{
    bool isLeapYear = false;

    if ((year % 4U) != 0U) {
        isLeapYear = false;
    } else if ((year % 100U) != 0U) {
        isLeapYear = true;
    } else if ((year % 400U) != 0U) {
        isLeapYear = false;
    } else {
        isLeapYear = true;
    }

    return isLeapYear;
}

/*******************************************************************************
 *** Function Name : RTC_ConfigICMTrigger
 *** Description   : config ICM trigger function
 ******************************************************************************/
void RTC_ConfigICMTrigger(uint32_t instance, bool enable)
{
    ASSERT_PARAM(instance < CONFIG_RTC_INSTANCE_COUNT);

    RTC_REG_SetTriggerEnable(s_rtcRegBase[instance], enable);
}

/*******************************************************************************
 *** Function Name : RTC_IsICMTriggerEnabled
 *** Description   : Check if ICM trigger function is enabled
 ******************************************************************************/
bool RTC_IsICMTriggerEnabled(uint32_t instance)
{
    ASSERT_PARAM(instance < CONFIG_RTC_INSTANCE_COUNT);

    return RTC_REG_IsTriggerEnabled(s_rtcRegBase[instance]);
}

/*******************************************************************************
 *** Function Name : RTC_IRQHandlerProcess
 *** Description   : Interrupt handler of RTC
 ******************************************************************************/
void RTC_IRQHandlerProcess(uint32_t instance)
{
    ASSERT_PARAM(instance < CONFIG_RTC_INSTANCE_COUNT);

    if (RTC_REG_GetAlarmIntFlag(s_rtcRegBase[instance])) {
        RTC_AlarmIRQHandlerProcess(instance);
    }

    if (RTC_REG_GetPeriodicAlarmIntFlag(s_rtcRegBase[instance])) {
        RTC_PeriodicAlarmIRQHandlerProcess(instance);
    }

    if (RTC_REG_GetOverflowIntFlag(s_rtcRegBase[instance])) {
        RTC_OverflowIRQHandlerProcess(instance);
    }
}

/*******************************************************************************
 *** Function Name : RTC_AlarmIRQHandlerProcess
 *** Description   : Alarm interrupt handler of RTC
 ******************************************************************************/
static void RTC_AlarmIRQHandlerProcess(uint32_t instance)
{
    ASSERT_PARAM(instance < CONFIG_RTC_INSTANCE_COUNT);

    uint32_t seconds;
    rtc_alarm_config_t* alarmConfig = s_rtcRunTimeConfig[instance].alarmConfig;

    RTC_REG_ClearAlarmIntFlag(s_rtcRegBase[instance]);

    if (alarmConfig != NULL) {
        /* If alarm is set to repeat mode, auto configure the next alarm */
        if (alarmConfig->numberOfRepeats > 0U) {
            s_rtcRunTimeConfig[instance].alarmRepeat = true;
            alarmConfig->numberOfRepeats--;
        } else if (alarmConfig->repeatForever) {
            s_rtcRunTimeConfig[instance].alarmRepeat = true;
        } else {
            s_rtcRunTimeConfig[instance].alarmRepeat = false;
        }

        if (s_rtcRunTimeConfig[instance].alarmRepeat) {
            /* RTC counter value need 3 RTC clock cycles sync to SCNT,
               so read alarm target is more reliable */
            seconds = RTC_REG_GetAlarmTarget(s_rtcRegBase[instance]);
            seconds += alarmConfig->repetitionInterval;

            RTC_REG_SetAlarmTarget(s_rtcRegBase[instance], seconds);
        } else {
            RTC_REG_SetAlarmTarget(s_rtcRegBase[instance], 0U);
            RTC_REG_SetAlarmIntEnable(s_rtcRegBase[instance], false);
            RTC_REG_SetAlarmEnable(s_rtcRegBase[instance], false);
        }

        if (alarmConfig->alarmCallback != NULL) {
            alarmConfig->alarmCallback(instance, alarmConfig->callbackParam);
        }
    }
}

/*******************************************************************************
 *** Function Name : RTC_PeriodicAlarmIRQHandlerProcess
 *** Description   : Periodic alarm interrupt handler of RTC
 ******************************************************************************/
static void RTC_PeriodicAlarmIRQHandlerProcess(uint32_t instance)
{
    ASSERT_PARAM(instance < CONFIG_RTC_INSTANCE_COUNT);

    const rtc_periodic_alarm_config_t* periodicAlarmConfig = s_rtcRunTimeConfig[instance].periodicAlarmConfig;

    RTC_REG_ClearPeriodicAlarmIntFlag(s_rtcRegBase[instance]);

    if ((periodicAlarmConfig != NULL) && (periodicAlarmConfig->periodicAlarmCallback != NULL)) {
        periodicAlarmConfig->periodicAlarmCallback(instance, periodicAlarmConfig->callbackParam);
    }
}

/*******************************************************************************
 *** Function Name : RTC_OverflowIRQHandlerProcess
 *** Description   : Overflow interrupt handler of RTC
 ******************************************************************************/
static void RTC_OverflowIRQHandlerProcess(uint32_t instance)
{
    ASSERT_PARAM(instance < CONFIG_RTC_INSTANCE_COUNT);

    const rtc_overflow_config_t* overflowConfig = s_rtcRunTimeConfig[instance].overflowConfig;

    RTC_REG_ClearOverflowIntFlag(s_rtcRegBase[instance]);

    if ((overflowConfig != NULL) && (overflowConfig->overflowCallback != NULL)) {
        overflowConfig->overflowCallback(instance, overflowConfig->callbackParam);
    }
}

/*******EOF********************************************************************/
